从旧版 Rive Android 运行时迁移
概述
新的 Rive Android 运行时是对公共 API 和内部架构的近乎完全的重写。虽然在概念上大多数操作都有等效项,但两个 API 不兼容。你在旧版 API 中任何想迁移的现有工作都必须使用新的 API 重新构建。本指南旨在演示如何迁移每个功能区域。
本指南涵盖:
- 共享功能 - 常见操作及其等效项。
- 新的独占功能 - 仅在新运行时中可用的功能。
- 旧版独占功能 - 新运行时不再支持的功能。大多数都有变通方法或替代方案;这些移除简化了 API 表面,减少了维护开销,并消除了反模式。
本指南并非详尽无遗,因为那样会与现有的通用文档重复。有关特定主题的更多详细信息,请参阅文档的相关部分。
包
旧版 Rive Android 运行时位于 app.rive.runtime.kotlin 包(+ .core)中。新运行时位于 app.rive 包(+ .core)中。这允许同名类(如 Artboard)共存而不冲突。如果你需要在同一文件中使用两个包中的同名类,可以使用 Kotlin 的 import as 功能为其中一个导入添加别名。
import app.rive.Artboard
import app.rive.runtime.kotlin.core.Artboard as LegacyArtboard
异步 API
由于基于 RiveWorker 的新线程模型,几乎所有在主线程上原本同步返回值的操作现在都是异步的 suspend 函数,例如加载 Rive 文件和执行查询。你需要从协程上下文中调用这些函数,例如在 Compose 的 LaunchedEffect 中,或使用 Activity 或 Fragment 中的 lifecycleScope.launch。
最常见的 Compose 操作被包装在辅助函数中,例如 rememberRiveFile,它将异步状态公开为 Result 类型以便在可组合项中更易于 使用。其他操作是"即发即弃"的,例如从加载的 Rive 文件创建画板和视图模型实例,或推进状态机。
生命周期
旧版 Rive Android 运行时基于 View,RiveAnimationView 类根据所属 Activity 或 Fragment 的生命周期管理其生命周期。Rive 文件的生命周期与视图的生命周期相关联,渲染器、控制器以及创建的对象(如画板和状态机)也是如此。这些使用分层引用计数来管理其生命周期。
新运行时使用 AutoCloseable 接口来显式管理对象(如 Rive 文件、画板和状态机)的生命周期。相比之下,worker 使用引用计数,并拥有其所创建资源的所有内存。手动创建对象时,你必须 close 它们以释放其资源,否则它们将在 worker 的生命周期内持续存在。或者,你可以将它们与 use 辅助函数一起使用,该函数将在其块末尾调用 close。worker 本身必须释放所有引用,否则它及其资源将会泄漏。
在 Compose 中,生命周期与 remember 可组合项绑定,并使用 DisposableEffect 根据需要创建和处置对象,简化了内存管理。
共享功能
共享的 API
一些 API 在旧版和新运行时之间是共享的。它们仍然位于旧版运行时的 app.rive.runtime.kotlin 包中。
- 用于初始化的
Rive全局对象,包括Rive.init方法。 RiveInitializerXML 初始化辅助工具。- 回退字体 API,包括
FontFallbackStrategy接口和FontFallbackStrategy.stylePicker全局变量。
这些共享的 API 将来可能会发生变化,变为在新包中具有修订版本的完全分离。这只会在主要版本发布时作为破坏性变更发生。
RiveAnimationView 到 Rive
旧版 Rive Android 运行时基于 View,提供了 RiveAnimationView 类来显示 Rive 动画。该名称反映了其创建时较有限的功能集,主要用于动画播放。
新 API 基于 Compose,计划将来提供 View API。主要的可组合项简称为 Rive,反映了其支持的更广泛的功能集和用例。
设置本地原始资源
新 API 将默认使用 I/O 调度器异步加载 Rive 文件。这与旧版运行时不同,旧版运行时在主线程上同步加载文件。如果你想使用不同的线程模型加载文件,请使用你首选的方法加载资源,并将字节传递给 RiveFileSource.Bytes 源。
旧版 RiveAnimationView
// Builder API
val riveView = RiveAnimationView.Builder(context)
.setResource(R.raw.my_rive_file)
.build()
// Set 方法
val riveView = RiveAnimationView(context)
riveView.setRiveResource(R.raw.my_rive_file)
旧版 XML 布局
<app.rive.runtime.kotlin.RiveAnimationView
app:riveResource="@raw/my_rive_file" />
新 Rive 可组合项
val riveWorker = rememberRiveWorker()
val riveFile = rememberRiveFile(
RiveFileSource.RawRes.from(R.raw.my_rive_file),
riveWorker
)
when (riveFile) {
is Result.Success -> Rive(riveFile.value)
else -> {} // 适当处理加载和错误状态
}
从字节设置 Rive 文件
旧版 RiveAnimationView
// Builder API
val riveView = RiveAnimationView.Builder(context)
.setResource(myBytes)
.build()
// Set 方法
val riveView = RiveAnimationView(context)
riveView.setRiveBytes(myBytes)
新 Rive 可组合项
val riveWorker = rememberRiveWorker()
val riveFile = rememberRiveFile(
RiveFileSource.Bytes(myBytes),
riveWorker
)
when (riveFile) {
is Result.Success -> Rive(riveFile.value)
else -> {} // 适当处理加载和错误状态
}
缓存 Rive 文件
有关更多详细信息,另请参阅缓存 Rive 文件。
在新运行时中,Rive 文件始终是显式创建的,而不是通常由视图隐式创建。这使得在多个 Rive 实例之间缓存和重用 Rive 文件更加明显。
旧版 RiveAnimationView
// 为简单起见在主线程加载字节;在生产环境中考虑在 后台线程加载。
val bytes = resources.openRawResource(R.raw.my_rive_file).use { res -> res.readBytes() }
val riveFile = File(bytes)
// Builder API
val riveView = RiveAnimationView.Builder(context)
.setResource(riveFile)
.build()
// Set 方法
val riveView = RiveAnimationView(context)
riveView.setRiveFile(riveFile)
// 如果不再需要该文件则释放,如果计划重用它则保留。
riveFile.release()
新 Rive 可组合项
由 rememberRiveFile 生成的 Rive 文件可以被提升到更高级 别的可组合项,以存活更长时间并在多个 Rive 实例之间共享。
val riveWorker = rememberRiveWorker()
val riveFile = rememberRiveFile(
RiveFileSource.RawRes.from(R.raw.my_rive_file),
riveWorker
)
when (riveFile) {
is Result.Success -> Rive(riveFile.value)
else -> {} // 适当处理加载和错误状态
}
选择画板和状态机
旧版 RiveAnimationView
// Builder API
val riveView = RiveAnimationView.Builder(context)
.setResource(R.raw.my_rive_file)
.setArtboardName("My Artboard")
.setStateMachineName("My State Machine")
.build()
// Set 方法
val riveView = RiveAnimationView(context)
riveView.setRiveResource(R.raw.my_rive_file,
artboardName = "My Artboard",
stateMachineName = "My State Machine"
)
旧版 XML 布局
<app.rive.runtime.kotlin.RiveAnimationView
app:riveResource="@raw/my_rive_file"
app:riveArtboard="My Artboard"
app:riveStateMachine="My State Machine" />
新 Rive 可组合项
画板和状态机作为对象而不是名称传递。你可以使用 rememberArtboard 和 rememberStateMachine 辅助函数从加载的 RiveFile 获取这些对象。
val riveWorker = rememberRiveWorker()
val riveFile = rememberRiveFile(
RiveFileSource.RawRes.from(R.raw.my_rive_file),
riveWorker
)
when (riveFile) {
is Result.Success -> {
val artboard = rememberArtboard(riveFile.value, "My Artboard")
val stateMachine = rememberStateMachine(artboard, "My State Machine")
Rive(
riveFile.value,
artboard = artboard,
stateMachine = stateMachine
)
}
else -> {} // 适当处理加载和错误状态
}
设置适配和对齐
有关更多详细信息,另请参阅布局文档。
旧版 RiveAnimationView
// Builder API
val riveView = RiveAnimationView.Builder(context)
.setResource(R.raw.my_rive_file)
.setFit(Fit.CONTAIN)
.setAlignment(Alignment.CENTER)
.build()
// Set 方法
val riveView = RiveAnimationView(context)
riveView.setRiveResource(R.raw.my_rive_file,
fit = Fit.CONTAIN,
alignment = Alignment.CENTER
)
// 属性
riveView.controller.fit = Fit.CONTAIN
riveView.controller.alignment = Alignment.CENTER
旧版 XML 布局
<app.rive.runtime.kotlin.RiveAnimationView
app:riveResource="@raw/my_rive_file"
app:riveFit="CONTAIN"
app:riveAlignment="CENTER" />
新 Rive 可组合项
新运行时将适配模式建模为密封类,仅在支持对齐的变体上允许设置对齐。此外,Layout 变体公开了布局缩放因子。
Rive(
riveFile,
fit = Fit.Contain(Alignment.Center),
)
自动绑定
有关更多详细信息,另请参阅数据绑定中的自动绑定部分。
旧版 RiveAnimationView
// Builder API
val riveView = RiveAnimationView.Builder(context)
.setResource(R.raw.my_rive_file)
.setAutoBind(true)
.build()
// Set 方法
val riveView = RiveAnimationView(context)
riveView.setRiveResource(R.raw.my_rive_file, autoBind = true)
旧版 XML 布局
<app.rive.runtime.kotlin.RiveAnimationView
app:riveResource="@raw/my_rive_file"
app:riveAutoBind="true" />
新 Rive 可组合项
由于可组合项的性质,Compose API 中没有实现自动绑定。由于它们是函数,与类相比很难从中获取值。回调需要一个空占位符来在触发前记住值,这比直接提供实例产生更多开销。
等效做法是创建一个无源的视图模型实例。这将在内部创建默认画板、该画板的默认视图模型以及该视图模型的默认实例。然后可以将其传递给 Rive 可组合项。
val vmi = rememberViewModelInstance(riveFile)
Rive(
riveFile,
viewModelInstance = vmi,
)
播放、暂停和状态机稳定
旧版 RiveAnimationView
旧版 API 在 RiveAnimationView 类上包含 play() 和 pause() 方法来控制动画的播放。值得注意的是,暂停状态与稳定状态相同,这并不理想。play() 将同时取消暂停和取消稳定状态机,使其推进。
riveView.play() // 取消稳定并播放
riveView.pause() // 暂停
新 Rive 可组合项
在新运行时中,没有 play 或 pause 方法。相反,播放状态的控制通过 Rive 可组合项上的 playing 参数完成。将 playing 设置为 false 将暂停动画,而将其设置为 true 将恢复播放。状态机的稳定和取消稳定是自动完成的,涵盖了所有需要它的场景。
var isPlaying by remember { mutableStateOf(true) }
Rive(
riveFile,
playing = isPlaying
)
自动播放和设置初始值
旧版运行时包含一个 autoplay 功能(默认为 true),在加载 Rive 文件时自动开始播放给定的动画或状态机。可以禁用它以在播放开始前设置初始值。
旧版 RiveAnimationView
// Builder API
val riveView = RiveAnimationView.Builder(context)
.setResource(R.raw.my_rive_file)
.setAutoPlay(true)
.build()
// Set 方法
val riveView = RiveAnimationView(context)
riveView.setRiveResource(R.raw.my_rive_file, autoPlay = true)
旧版 XML 布局
<app.rive.runtime.kotlin.RiveAnimationView
app:riveResource="@raw/my_rive_file"
app:riveAutoPlay="true" />
新运行时没有包含 autoplay 功能,因为它不是必需的——Rive 文件与其展示是解耦的。你可以将 playing 保留为其默认值 true 以实现类似行为。要在播放开始前设置初始值,可以将 playing 设置为 false,更新数据绑定的属性,然后将 playing 设置为 true 开始播放。
var isPlaying by remember { mutableStateOf(false) }
val vmi = rememberViewModelInstance(riveFile)
LaunchedEffect(vmi) {
// 设置初始值
vmi.setNumber("My Number Property", 42.0f)
// 开始播放
isPlaying = true
}
Rive(
riveFile,
playing = isPlaying,
viewModelInstance = vmi,
)
触摸穿透
旧版 RiveAnimationView
// Builder API
val riveView = RiveAnimationView.Builder(context)
.setResource(R.raw.my_rive_file)
.setTouchPassThrough(true)
.build()
// 属性
riveView.touchPassThrough = true
旧版 XML 布局
<app.rive.runtime.kotlin.RiveAnimationView
app:riveResource="@raw/my_rive_file"
app:riveTouchPassThrough="true" />
新 Rive 可组合项
新运行时在 Rive 可组合项上提供了 touchPassThrough 参数。这更加细致,可以设置为 Consume(默认行为)、Observe 或 PassThrough。Observe 仅与 Compose 层次结构中的祖先共享,适用于不阻止滚动事件,而 PassThrough 还与同级共享(等效于旧版行为)。
Rive(
riveFile,
touchPassThrough = RivePointerInputMode.PassThrough,
)
视图模型
旧版
旧版运行时包含一个从 File 检索的 ViewModel 类,对应于 Rive 编辑器中的视图模型。此类提供了查询和创建视图模型实例的方法。
val viewModel = riveFile.getViewModelByName("My View Model")
val instance = viewModel.createInstanceFromName("My Instance")
val properties = viewModel.properties
新版
新运行时将查询 合并到 RiveFile 类中,在查询需要时包含视图模型名称。视图模型实例由 ViewModelSource 指定,可以是 Named 或 DefaultForArtboard。然后,该 ViewModelSource 可以使用 blankInstance、defaultInstance 或 namedInstance 辅助函数转换为 ViewModelInstanceSource。完成的源可以传递给 rememberViewModelInstance 或 ViewModelInstance.fromFile。
val instanceSource = ViewModelSource.Named("My View Model").namedInstance("My Instance")
val instance = rememberViewModelInstance(riveFile, instanceSource)
LaunchedEffect(riveFile) {
val properties = riveFile.getViewModelProperties("My View Model")
}